# frozen_string_literal: true

require_relative '../../../spec_helper'
require 'wpxf/cli/output'

describe Wpxf::Cli::Output do
  let(:klass) do
    Class.new do
      include Wpxf::Cli::Output

      def initialize
        super
      end
    end
  end

  let(:subject) { klass.new }
  let(:col_widths) { { 'col1' => 5, 'col2' => 5 } }
  let(:data) do
    [
      { 'col1' => 'Col 1', 'col2' => 'Col 2' },
      { 'col1' => 'row 1 col 1', 'col2' => 'row 1 col 2' },
      { 'col1' => 'row 2 col 1 with padding', 'col2' => 'row 2 col 2' },
      { 'col1' => 'row 3 col 1', 'col2' => 'row 3 column 2' }
    ]
  end

  before :each, 'setup mocks' do
    allow(subject).to receive(:puts)
    allow(subject).to receive(:print)
    allow(subject).to receive(:wrap_text).and_call_original
    allow(subject).to receive(:print_table_row).and_call_original
    allow(subject).to receive(:print_header_separator).and_call_original
  end

  describe '#new' do
    it 'should initialise #indent as two spaces' do
      subject = klass.new
      expect(subject.indent).to eql '  '
    end

    it 'should initialise #indent_level to 1' do
      subject = klass.new
      expect(subject.indent_level).to eql 1
    end
  end

  describe '#indent_cursor' do
    it 'should increase #indent_level by `level` and yield the block' do
      did_yield = false
      subject.indent_cursor(5) do
        did_yield = true
        expect(subject.indent_level).to eql 6
      end

      expect(did_yield).to be true
    end

    it 'should decrease #indent_level after the block has finished executing' do
      subject.indent_cursor(5) {}
      expect(subject.indent_level).to eql 1
    end
  end

  describe '#remove_new_lines_and_wrap_text' do
    it 'should return the indented text with new lines removed' do
      subject.indent_level = 2
      res = subject.remove_new_lines_and_wrap_text("line 1\nline2\nline3")
      expect(res).to eql 'line 1line2line3'
    end

    it 'should return the indented text with lines >= `width` wrapped' do
      subject.indent_level = 2
      res = subject.remove_new_lines_and_wrap_text('12345 67891 0', 0, 5)
      expect(res).to eql "12345\n    67891\n    0"
    end

    it 'should not break words that exceed the max line length' do
      subject.indent_level = 2
      res = subject.remove_new_lines_and_wrap_text('12345678910', 0, 5)
      expect(res).to eql '12345678910'
    end

    it 'should add blank spaces after new lines if `padding` is specified' do
      subject.indent_level = 1
      res = subject.remove_new_lines_and_wrap_text('12345 67891 0', 10, 5)
      expect(res).to eql "12345\n            67891\n            0"
    end

    it 'should default to a `width` of 78' do
      first_line = ''
      (1..78).each { |i| first_line += i.to_s }
      res = subject.remove_new_lines_and_wrap_text("#{first_line} new line")
      expect(res).to eql "#{first_line}\n  new line"
    end
  end

  describe '#wrap_text' do
    it 'should wrap and indent lines longer than or equal to `width`' do
      subject.indent_level = 2
      res = subject.wrap_text('12345 67891 0', 0, 5)
      expect(res).to eql "12345\n    67891\n    0"
    end

    it 'should add blank spaces after new lines if `padding` is specified' do
      subject.indent_level = 1
      res = subject.wrap_text('12345 67891 0', 10, 5)
      expect(res).to eql "12345\n            67891\n            0"
    end

    it 'should default to a `width` of 78' do
      first_line = ''
      (1..78).each { |i| first_line += i.to_s }
      res = subject.wrap_text("#{first_line} new line")
      expect(res).to eql "#{first_line}\n  new line"
    end
  end

  describe '#indent_without_wrap' do
    it 'should return the indented text without automated wrapping' do
      subject.indent_level = 2
      res = subject.indent_without_wrap("line 1\nline2\nline3")
      expect(res).to eql "line 1\n    line2\n    line3"
    end
  end

  describe '#print_std' do
    it 'should print `msg` prefixed by #indent_level * #indent' do
      subject.print_std 'test'
      expect(subject).to have_received(:puts).with('  test').exactly(1).times
    end
  end

  describe '#print_info' do
    it 'should print a light blue hyphen prefix and indent' do
      subject.print_info 'test'
      expect(subject).to have_received(:print)
        .with('  [-] '.light_blue)
        .exactly(1).times
    end

    it 'should print `msg` with a padding of 4 and wrap width of 90' do
      subject.print_info 'test'
      expect(subject).to have_received(:wrap_text)
        .with('test', 4, 90)
        .exactly(1).times
    end
  end

  describe '#print_good' do
    it 'should print a green plus prefix and indent' do
      subject.print_good 'test'
      expect(subject).to have_received(:print)
        .with('  [+] '.green)
        .exactly(1).times
    end

    it 'should print `msg` with a padding of 4 and wrap width of 90' do
      subject.print_good 'test'
      expect(subject).to have_received(:wrap_text)
        .with('test', 4, 90)
        .exactly(1).times
    end
  end

  describe '#print_bad' do
    it 'should print a red exclamation mark prefix and indent' do
      subject.print_bad 'test'
      expect(subject).to have_received(:print)
        .with('  [!] '.red)
        .exactly(1).times
    end

    it 'should print `msg` with a padding of 4 and wrap width of 90' do
      subject.print_bad 'test'
      expect(subject).to have_received(:wrap_text)
        .with('test', 4, 90)
        .exactly(1).times
    end
  end

  describe '#print_warning' do
    it 'should print a yellow exclamation mark prefix and indent' do
      subject.print_warning 'test'
      expect(subject).to have_received(:print)
        .with('  [!] '.yellow)
        .exactly(1).times
    end

    it 'should print `msg` with a padding of 4 and wrap width of 90' do
      subject.print_warning 'test'
      expect(subject).to have_received(:wrap_text)
        .with('test', 4, 90)
        .exactly(1).times
    end
  end

  describe '#print_table' do
    it 'should print the table headers' do
      allow(subject).to receive(:print_table_header)
      subject.print_table data
      expect(subject).to have_received(:print_table_header)
        .with(data, anything)
        .exactly(1).times
    end

    it 'should print each table row' do
      allow(subject).to receive(:print_table_row)
      subject.print_table data

      (1..3).each do |i|
        expect(subject).to have_received(:print_table_row)
          .with(data[i], anything)
          .exactly(1).times
      end
    end

    it 'should print a new line after each table row' do
      allow(subject).to receive(:print_table_header)
      allow(subject).to receive(:print_table_row)
      subject.print_table data
      expect(subject).to have_received(:puts).exactly(3).times
    end

    context 'if `pad_with_new_lines` is true' do
      it 'should print new lines around the table' do
        allow(subject).to receive(:print_table_header)
        allow(subject).to receive(:print_table_row)

        subject.print_table [].push(data[0]), true
        expect(subject).to have_received(:puts).exactly(2).times
      end
    end
  end

  describe '#print_table_header' do
    it 'should print the table row at index 0 of `data`' do
      subject.print_table_header data, col_widths
      expect(subject).to have_received(:print_table_row)
        .with(data[0], col_widths)
        .exactly(1).times
    end

    it 'should print the header separator' do
      subject.print_table_header data, col_widths
      expect(subject).to have_received(:print_header_separator)
        .with(col_widths)
        .exactly(1).times
    end
  end

  describe '#calculate_col_widths' do
    it 'should return a hash that has the same keys as `data`' do
      res = subject.calculate_col_widths(data)
      expect(res.keys).to include('col1', 'col2')
    end

    it 'should calculate and store the length of the largest strings' do
      res = subject.calculate_col_widths(data)
      expect(res['col1']).to eql 24
      expect(res['col2']).to eql 14
    end
  end

  describe '#print_header_separator' do
    it 'should print a row of hyphens for each column' do
      subject.print_header_separator(col_widths)
      expect(subject).to have_received(:print_table_row)
        .with({ 'col1' => '-----', 'col2' => '-----' }, col_widths)
        .exactly(1).times
    end
  end

  describe '#print_table_row' do
    it 'should print an initial indent' do
      widths = subject.calculate_col_widths(data)
      subject.print_table_row(data[1], widths)
      expect(subject).to have_received(:print)
        .with('  ')
        .exactly(1).times
    end

    it 'should print the column data with padding to align the next column' do
      widths = subject.calculate_col_widths(data)
      subject.print_table_row(data[2], widths)
      expect(subject).to have_received(:print)
        .with('row 2 col 1 with padding   ')
        .exactly(1).times

      expect(subject).to have_received(:print)
        .with('row 2 col 2      ')
        .exactly(1).times
    end
  end
end
